package com.sunrate.investingquote.util;

import com.alibaba.fastjson2.JSON;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.ql.util.express.Operator;
import com.sunrate.investingquote.annotation.RuleLable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

/**
 * @author yyh
 * @description:
 * @date 2023/06/28/ 18:17
 */
@Slf4j
public class QlExpressUtil {

    private static final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(true, false);

    static {
        EXPRESS_RUNNER.addFunction("bankCode", new BankCodeOperator());
    }

    private QlExpressUtil() {
        throw new IllegalStateException("QlExpressUtil class create error");
    }

    @Deprecated
    public static IExpressContext<String, Object> convertToContext(Object qlExpressContext) {
        try {
            DefaultContext<String, Object> context = new DefaultContext<>();
            Field[] declaredFields = qlExpressContext.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                ReflectionUtils.makeAccessible(field);
                RuleLable annotation = field.getAnnotation(RuleLable.class);
                if (annotation == null || StringUtils.isEmpty(annotation.code())) {
                    continue;
                }
                context.put(annotation.code(), field.get(qlExpressContext));
            }
            return context;
        } catch (Exception e) {
            log.error("QlExpressUtil beanToContext error", e);
        }
        return null;
    }

    public static IExpressContext<String, Object> beanToContext(Object bean) {
        DefaultContext<String, Object> context = new DefaultContext<>();
        BeanMap beanMap = BeanMap.create(bean);
        context.putAll(beanMap);
        return context;
    }


    public static <T> T execute(String expression, IExpressContext<String, Object> context) throws Exception {
        T execute = (T) EXPRESS_RUNNER.execute(expression, context, null, true, true);
        log.info("execute result:{},express:{},context:{}", execute, expression, JSON.toJSONString(context));
        return execute;
    }


    public static class BankCodeOperator extends Operator {
        @Override
        public Object executeInner(Object[] objects) {
            if (objects.length < 4) {
//                throw new RuntimeException("[BankCode] lenth<4");
                return false;
            }
            String swiftCode = (String) objects[0];
            String target = (String) objects[1];
            int start = (int) objects[2];
            int end = (int) objects[3];
            int size = end - start + 1;
            if (size > swiftCode.length()) {
//                throw new RuntimeException("[BankCode] size > BankCode.length()");
                return false;
            }
            int remian = swiftCode.length() - start + 1;
            if (remian < size) {
//                throw new RuntimeException("[BankCode] remian < size");
                return false;
            }
            return StringUtils.equals(swiftCode.substring(start, end + 1), target);
        }
    }

}
